/** @file oct_object.h
    @brief Octree object */

#ifndef G3_OCT_OBJECT_H
#define G3_OCT_OBJECT_H

#include "GLee/GLee.h"

#include "file/f_zipstream.h"

#include "vbo.h"
#include "geompointer.h"
#include "render/batch/b_renderer.h"
#include "res/res_media_streamer.h"

#include "oct/oct_node.h"
#include "oct/oct_leaf.h"

namespace G3 {

class OCT_FILE_HEADER {
    public:
        OCT_FILE_HEADER ();
        OCT_FILE_HEADER (const OCT_FILE_HEADER &hdr);

        OCT_FILE_HEADER& operator= (const OCT_FILE_HEADER &hdr);

        byte magic [4];

        int num_verts;
        int num_texels;
        int num_normals;
        int num_tangents;
        int num_indexes;
        int num_materials;
        long materialchunk_size;
        int num_nodes;
        int num_leaves;
        int num_batches;

        bool check_magic ();
        void dump (G3::LOG *log);
};

class OCT_FILE_NODE {
    public:
        OCT_FILE_NODE ();
        OCT_FILE_NODE (const OCT_FILE_NODE &node);

        OCT_FILE_NODE& operator= (const OCT_FILE_NODE &node);

        G3::AABB3F BBox;
        int children [8];
};

class OCT_FILE_LEAF {
    public:
        OCT_FILE_LEAF ();
        OCT_FILE_LEAF (const OCT_FILE_LEAF &leaf);

        OCT_FILE_LEAF& operator= (const OCT_FILE_LEAF &leaf);

        G3::AABB3F BBox;
        int num_batches;
        int batch_offset;
};

class OCT_FILE_BATCH {
    public:
        OCT_FILE_BATCH ();
        OCT_FILE_BATCH (const OCT_FILE_BATCH &batch);

        OCT_FILE_BATCH& operator= (const OCT_FILE_BATCH &batch);

        G3::AABB3F BBox;
        int matID;
        int indexStart;
        int numElements;
};

class OCT_OBJECT: public SPATIAL_RENDERABLE, public MEDIA_OBJECT {
    public:
        OCT_OBJECT ();
        OCT_OBJECT (const OCT_OBJECT &obj);
        ~OCT_OBJECT ();

        OCT_FILE_HEADER fHeader;

        // 1 is the root node
        std::vector<G3::OCT_NODE> nodes;
        std::vector<G3::OCT_LEAF> leaves;
        std::vector<G3::BATCH> batches;
        std::vector<G3::VEC3F> vertices;
        std::vector<G3::VEC2F> texcoords;
        std::vector<G3::VEC3F> normals;
        std::vector<G3::VEC3F> tangents;
        std::vector<unsigned int> indexes;
        std::vector<G3::MATERIAL> materials;
        std::vector<std::string> material_files;

        VBO *vbo;
        DISPLAYLIST *dlist;
        GEOM_POINTER *gptr;

        G3::LOG *log;

        OCT_OBJECT& operator= (const OCT_OBJECT &obj);

        bool save (const std::string &filename);
        bool load (const std::string &filename);

        void upload_node (int id, G3::VEC3F pos);
        void upload_remove_all ();

        void load_materials ();

        void debug_render ();

        void update (const G3::FRUSTUM *frust);

        void build_common ();

        void build_vbo ();
        void destroy_vbo ();

        void build_dlist ();
        void destroy_dlist ();

        void build_gptr ();
        void destroy_gptr ();

        READ_RESULT read ();
        bool clear ();

        void readHeader ();
        void readMaterials ();
        void readNodes ();
        void readLeaves ();
        void readBatches ();

        void writeHeader ();
        void writeMaterials ();
        void writeNodes ();
        void writeLeaves ();
        void writeBatches ();

        void dump ();

    private:
        /** A pointer to the materials array */
        G3::MATERIAL *mat;
        /** A pointer to the texcoords array */
        G3::VEC2F *uv;
        /** A pointer to the normals array */
        G3::VEC3F *n;
        /** A pointer to the tangents array */
        G3::VEC3F *t;

        G3::ZIPSTREAM *zStream;
        G3::CISTREAM *unzStream;
};

}

#endif // G3_OCT_OBJECT_H
